#include "PrecompiledHeaders.h"
#include "HighScore.h"
#include "Replay.h"
#include "Utilities.h"
#include "GameResources.h"

/*
 * Constructor
 */
HighScore::HighScore(void)
{
    std::string res_folder (Constants::RESOURCES_FOLDER);
    currentFile = std::string(res_folder + "scores/scores.highscores");
    readFromFile(currentFile);
}

/*
 * Destructor
 */
HighScore::~HighScore(void)
{
    saveToFile(currentFile);
}

/*
 * Checks whether a new high score has been achieved
 */
bool HighScore::isHighScore(std::string scenario, int score)
{
    ScoreVector *scoreVector = &scoremap[scenario];
    
    // If there are blank entries
    if (scoreVector->size() < NUM_HIGH_SCORES) {
        return true;
    }
    
    // Othereise check the lowest score
    ScoreVector::iterator vi;
    for (vi = scoreVector->begin(); vi != scoreVector->end(); vi++)
    {
        if (score > vi->score)
            return true;
    }
    return false;
}

/*
 * Adds new highscore to a ScoreVector
 */
bool HighScore::addNewScore(std::string level, int score, std::string name, std::string replay)
{
    ScoreEntry newEntry;
    newEntry.score = score;
	newEntry.name = name;
    newEntry.replay = replay;
    ScoreVector *scoreVector = &scoremap[level];
	
    // If there is space for a new highscore
    if (scoreVector->size() < NUM_HIGH_SCORES)
    {
        scoreVector->push_back(newEntry);
		saveToFile(currentFile);
        return true;
    }
    
    // Or try and replace lowest highscore
    ScoreVector::iterator hold, vi = scoreVector->begin();
    int lowScore = vi->score;
    for (vi = scoreVector->begin(); vi != scoreVector->end(); vi++)
    {
        if(vi->score < lowScore) {
            lowScore = vi->score;
            hold = vi;
        }
    }
    if(score > lowScore) {
		scoreVector->erase(hold);
        //scoreVector->insert(vi, newEntry);
		scoreVector->push_back(newEntry);
		saveToFile(currentFile);
        return true;
    }
    
    // sort the scores
    //sort(scoreVector->begin(), scoreVector->end(), compareScoreEntries);
	saveToFile(currentFile);
    return false;
}

/*
 * Gets the vector of scores for the level given in the argument
 */
ScoreVector *HighScore::getScoreSet(std::string level)
{
    ScoreVector *scoreVector = &scoremap[level];
    sort(scoreVector->begin(), scoreVector->end(), compareScoreEntries);
    return scoreVector;
}

/*
 * Read the specified file into the map of ScoreVectors
 */
void HighScore::readFromFile(std::string filename)
{
    if(!fileExists(filename)) {
        printf("No highscore file, initialising blank set.\n");
        //initialiseNewHighScores();
        return;
    }
    
    std::ifstream file;
    file.open(filename.c_str(), std::ios_base::binary);
    
    // Test if the file is empty
    file.seekg(0, std::ios::end);
    if ((int)file.tellg() == 0) {
         printf("Empty score file\n");
         file.close();
         return;
    }
    file.seekg(0, std::ios_base::beg);
    
    // Check for errors
    if(!file.is_open()) {
        fprintf(stderr, "Error: could not open high score file %s for reading\n", filename.c_str());
    }
    
    // Read contents of file
    int score, strLen, i=0;
    char string[STRING_BUF_LEN];
    std::string level;
    while(!file.eof())
    {
        ScoreEntry newEntry;
        
        // read length and name of the level
        file.read((char *)&strLen, sizeof(int));
        file.read((char *)&string, strLen);
        string[strLen] = '\0';
        level = std::string(string);
        
        // read length of name and then name
        file.read((char *)&strLen, sizeof(int));
        file.read((char *)&string, strLen);
        string[strLen] = '\0';
        newEntry.name = string;
        
        // read score
        file.read((char *)&score, sizeof(int));
        newEntry.score = score;
        
        // read replay file name
        //file.read((char *)&string, REPLAY_FILENAME_LENGTH);
        //string[REPLAY_FILENAME_LENGTH] = '\0';
		file.read((char *)&strLen, sizeof(int));
		file.read((char *)&string, strLen);
        string[strLen] = '\0';
        newEntry.replay = std::string(string);
        
        // Add to the scoremap
        scoremap[level].push_back(newEntry);
        i++;
    }
    file.close();
    printf("Finished loading %i scores\n", i);
}

/*
 * Add levels to highscore data structure
 */
void HighScore::initialiseNewHighScores()
{
    LevelVector::iterator li;
    for(li=GameResources::levels.begin(); li!=GameResources::levels.end(); li++) {
        // blah
    }
}

/*
 * Saves the contents of the scoremap to the highscore file
 */
void HighScore::saveToFile(std::string filename)
{
    // Open the file
    std::ofstream file;
    file.open(filename.c_str(), std::ios::binary);
    
    // Check for errors
    if(!file.is_open()) {
        fprintf(stderr, "Error: could not open high score file %s for writing", filename.c_str());
    }
    
    // Write to file
    ScoreMap::iterator mi;
    for (mi = scoremap.begin(); mi != scoremap.end();  mi++)
    {
        ScoreVector *scoreVector = &mi->second;
        ScoreVector::iterator vi;
        std::string level = mi->first;
        int strLen;
        for (vi = scoreVector->begin(); vi != scoreVector->end(); vi++)
        {
            // write str length level name and the string
            strLen = (int)level.size();
            file.write((char *)&strLen, sizeof(int));
            file.write(level.c_str(), strLen);
            
            // write str length of name and then the string
            strLen = (int)vi->name.size();
            file.write((char *)&strLen, sizeof(int));
            file.write(vi->name.c_str(), strLen);
            
            // write score
            file.write((char *)&vi->score, sizeof(int));
            
            // write the replay filename
            //file.write(vi->replay.c_str(), REPLAY_FILENAME_LENGTH);
			strLen = (int)vi->replay.size();
			file.write((char *)&strLen, sizeof(int));
			file.write(vi->replay.c_str(), strLen);
        }
    }
    file.close();
}

/*
 * Retrieves the top score of a scenario
 */
std::string HighScore::getTopScore(std::string level)
{
	ScoreVector *scoreVector = getScoreSet(level);
	if (scoreVector->size() == 0) {
		return "000000";
	} else {
		return formatScore(scoreVector->begin()->score);
	}
}

std::string HighScore::getTopScoreReplay(std::string level)
{
    ScoreVector *scoreVector = getScoreSet(level);
    if (scoreVector->size() == 0)
		return "";
	else
	    return scoreVector->begin()->replay;
}

/*
 * Changes the level image and level name based upon the current level in the iterator
 */
void HighScore::setScores(std::string level)
{
	ScoreVector *scoreVector = getScoreSet(level);
	ScoreVector::iterator vi = scoreVector->begin();
	for (int i=0; i<NUM_HIGH_SCORES; i++)
	{
        std::stringstream out;
        out << (i+1);
        std::string s = out.str();
        
        // Output scores if the exist, blank otherwise
	    if (i < (int)scoreVector->size())
	    {
	        changeScoreEntry("EmbeddedHighScores/" + s + "Name",
	            "EmbeddedHighScores/" + s + "Score", vi->name,
	            vi->score);
	        vi++;
	    }
	    else
	    {
	        changeScoreEntry("EmbeddedHighScores/" + s + "Name",
	            "EmbeddedHighScores/" + s + "Score", "----------", 0);
	    }
	}
}

/*
 * Changes the text in a score entry window. Changes the score and name.
 */
void HighScore::changeScoreEntry(std::string nameLabel, std::string scoreLabel, std::string name, int score)
{
    CEGUI::Window *label = CEGUI::WindowManager::getSingletonPtr()->getWindow(nameLabel);
    label->setProperty("Text", name);
    label = CEGUI::WindowManager::getSingletonPtr()->getWindow(scoreLabel);
    label->setProperty("Text", formatScore(score));
}

void HighScore::setReplays(std::string level)
{
	if (level == "game")
	{
		disableReplay("1Replay");
		disableReplay("2Replay");
		disableReplay("3Replay");
		disableReplay("4Replay");
		disableReplay("5Replay");
		return;
	}
	ScoreVector *scoreVector = getScoreSet(level);
	ScoreVector::iterator vi = scoreVector->begin();
	for (int i=0; i<NUM_HIGH_SCORES; i++)
	{
        std::stringstream out;
        out << (i+1);
        std::string s = out.str();
        
        // Output scores if the exist, blank otherwise
	    if (i < (int)scoreVector->size())
	    {
	        enableReplay(s + "Replay", vi->replay);
	        vi++;
	    }
	    else
	    {
			disableReplay(s + "Replay");
	    }
	}
}

// Enables a replay button with the associated replay
void HighScore::enableReplay(std::string replayLabel, std::string replay)
{
	CEGUI::Window *label = CEGUI::WindowManager::getSingletonPtr()->getWindow(replayLabel);
    label->setProperty("Visible", "True");
	label->setText(replay);
}

// Disables a replay button when there is no high score in that position
void HighScore::disableReplay(std::string replayLabel)
{
	CEGUI::Window *label = CEGUI::WindowManager::getSingletonPtr()->getWindow(replayLabel);
    label->setProperty("Visible", "False");
}

/*
 * Add leading 0s and return as a string
 */
std::string HighScore::formatScore(int score)
{
	std::stringstream out;
	out.fill('0');
	out.width(6);
	out << score;
	return out.str();	
}

/*
 * Boolean comparison of two ScoreEntries
 */
bool compareScoreEntries(ScoreEntry x, ScoreEntry y)
{
    if(x.score > y.score) return true;
    else return false;
}

// Returns a ranking based on the score
std::string HighScore::getRanking(int score)
{
	if (score < 500)
	{
		return "YOU'RE BALLS!";
	}
	else if (score < 1000)
	{
		return "ORBFUL!";
	}
	else if (score < 2000)
	{
		return "ORB-KAY!";
	}
	else if (score < 4000)
	{
		return "ORBSOME!";
	}
	else if (score < 6000)
	{
		return "OUT OF THIS ORB!";
	}
	else
	{
		return "YOU ARE ORB!";
	}
}

// Gets the colour code for the Ranking label based on the score
std::string HighScore::getRankingColour(int score)
{
	if (score < 500)
	{
		return "FFFF0000";
	}
	else if (score < 1000)
	{
		return "FFDF5A71";
	}
	else if (score < 2000)
	{
		return "FFAFFFCD";
	}
	else if (score < 4000)
	{
		return "FF31FF7F";
	}
	else if (score < 6000)
	{
		return "FF12FF00";
	}
	else
	{
		return "FFF6ED00";
	}
}
